package com.genius.utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * list转树形结构数据工具类
 */
public class TreeUtils {
    /**
     * 默认ID字段
     */
    private static final String DEFAULT_ID = "id";
    /**
     * 默认父级id字段
     */
    private static final String DEFAULT_PARENT_ID = "parentId";
    /**
     * 默认子级 集合字段
     */
    private static final String DEFAULT_CHILDREN = "children";
    /**
     * 顶级节点的 父级id
     */
    private static final Object TOP_PARENT_ID = 0;

    /**
     * 获取树形结构数据
     * @param list 要转换的集合
     * @param idName 实体id字段名
     * @param <T> 泛型
     * @return 转换后的集合
     * @throws Exception 产生的异常
     */
    public static <T> List<T> getChild(List<T> list, String idName) throws Exception {
        if(idName == null){
            return getChild(list, DEFAULT_ID, TOP_PARENT_ID, DEFAULT_PARENT_ID, DEFAULT_CHILDREN);
        }else{
            return getChild(list, idName, TOP_PARENT_ID, DEFAULT_PARENT_ID, DEFAULT_CHILDREN);
        }
    }

    /**
     * 集合转树形结构方法
     * @param list      要转换的集合
     * @param idName    实体id字段名
     * @param parentId  父级id
     * @param pIdName   实体父级id字段名
     * @param childName 实体子集集合字段名
     * @param <T> 泛型
     * @return 转换后的集合
     * @throws Exception 产生的异常
     */
    public static <T> List<T> getChild(List<T> list, String idName, Object parentId, String pIdName, String childName) throws Exception {
        // 子菜单
        List<T> childList = new ArrayList<>();
        for (T t : list) {
            Class<?> aClass = t.getClass();
            Field pId = aClass.getDeclaredField(pIdName);
            pId.setAccessible(true);
            Object o = pId.get(t);
            if (o == null) {
                continue;
            }
            if (String.valueOf(o).equals(String.valueOf(parentId))) {
                childList.add(t);
            }
        }
        // 把子菜单的子菜单再循环一遍
        for (T t : childList) {
            Class<?> aClass = t.getClass();
            Field pId = aClass.getDeclaredField(idName);
            pId.setAccessible(true);
            Object o = pId.get(t);
            List<T> childLis = getChild(list, idName, String.valueOf(o), pIdName, childName);
            Field child = aClass.getDeclaredField(childName);
            child.setAccessible(true);
            child.set(t, childLis);
        }
        // 判断递归结束
        if (childList.size() == 0) {
            return null;
        }
        return childList;
    }

    /**
     * 获取树形结构数据
     * @param list 要转换的集合
     * @param idName 实体id字段名
     * @param <T> 泛型
     * @return 转换后的集合
     * @throws Exception 产生的异常
     */
    public static <T> List<T> buildTree(List<T> list, String idName) throws Exception {
        if(idName == null){
            return buildTree(list, DEFAULT_ID, DEFAULT_PARENT_ID, DEFAULT_CHILDREN);
        }else{
            return buildTree(list, idName, DEFAULT_PARENT_ID, DEFAULT_CHILDREN);
        }
    }

    /**
     * 构建树形结构
     * @param list 要转换的集合
     * @param idName 实体id字段名
     * @param pIdName 实体父级id字段名
     * @param childName 实体子集集合字段名
     * @return 转换后的集合
     * @param <T> 泛型
     * @throws Exception 产生的异常
     */
    public static <T> List<T> buildTree(List<T> list, String idName, String pIdName,  String childName) throws Exception {
        List<T> treeNodes = new ArrayList<>();
        List<T> rootNodes = getRootNodes(list,idName,pIdName);
        for (T rootNode : rootNodes) {
            buildChildNodes(rootNode,list,idName,pIdName,childName);
            treeNodes.add(rootNode);
        }
        return treeNodes;
    }

    /**
     * 递归子节点
     * @param node 节点
     * @param list 要转换的集合
     * @param idName 实体id字段名
     * @param pIdName 实体父级id字段名
     * @param childName 实体子集集合字段名
     * @param <T> 泛型
     * @throws Exception 产生的异常
     */
    private static <T> void buildChildNodes(T node, List<T> list, String idName, String pIdName, String childName) throws Exception {
        List<T> children = getChildNodes(node,list,idName,pIdName);
        if (!children.isEmpty()) {
            for (T child : children) {
                buildChildNodes(child,list,idName,pIdName,childName);
            }
            Class<?> aClass = node.getClass();
            Field child = aClass.getDeclaredField(childName);
            child.setAccessible(true);
            child.set(node, children);
        }
    }

    /**
     * 获取父节点下所有的子节点
     * @param pNode 父节点
     * @param list 要转换的集合
     * @param idName 实体id字段名
     * @param pIdName 实体父级id字段名
     * @return 所有子节点
     * @param <T> 泛型
     * @throws Exception 产生的异常
     */
    private static <T> List<T> getChildNodes(T pNode,List<T> list, String idName, String pIdName) throws Exception {
        List<T> childNodeList = new ArrayList<>();
        Class<?> pNodeClass = pNode.getClass();
        Field idField = pNodeClass.getDeclaredField(idName);
        idField.setAccessible(true);
        Object id = idField.get(pNode);
        for (T t : list) {
            Class<?> tClass = t.getClass();
            Field pIdField = tClass.getDeclaredField(pIdName);
            pIdField.setAccessible(true);
            Object pId = pIdField.get(t);
            if (String.valueOf(id).equals(String.valueOf(pId))) {
                childNodeList.add(t);
            }
        }
        return childNodeList;
    }

    /**
     * 获取集合中所有的根节点
     * @param list 要转换的集合
     * @param idName 实体id字段名
     * @param pIdName 实体父级id字段名
     * @return 所有根节点
     * @param <T> 泛型
     * @throws Exception 产生的异常
     */
    private static <T> List<T>  getRootNodes(List<T> list, String idName, String pIdName) throws Exception{
        List<T> rootNodeList = new ArrayList<>();
        for (T t : list) {
            Class<?> tClass = t.getClass();
            Field pidField = tClass.getDeclaredField(pIdName);
            pidField.setAccessible(true);
            Object pid = pidField.get(t);
            if (isRootNode(list,idName,pid)) {
                rootNodeList.add(t);
            }
        }
        return rootNodeList;
    }

    /**
     * 判断是否为根节点
     * @param list 要转换的集合
     * @param idName 实体id字段名
     * @param parentId 实体父级id
     * @return 是否为根节点
     * @param <T> 泛型
     * @throws Exception 产生的异常
     */
    private static<T> boolean isRootNode(List<T> list, String idName, Object parentId) throws Exception{
        for (T t : list) {
            Class<?> aClass = t.getClass();
            Field id = aClass.getDeclaredField(idName);
            id.setAccessible(true);
            Object o = id.get(t);
            if (String.valueOf(parentId).equals(String.valueOf(o))) {
                return false;
            }
        }
        return true;
    }

}